#include <boost/assign.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

#include "../GameStateLoader.h"

using namespace snake;

namespace
{

  ECellContents CellFromChar(wchar_t ch)
  {
    using namespace snake::encoding;
    static const std::map<wchar_t, ECellContents> map = boost::assign::map_list_of
      (CHAR_EMPTY, CELL_EMPTY)
      (CHAR_WALL, CELL_WALL)
      (CHAR_SNAKE_BODY, CELL_SNAKE_BODY)
      (CHAR_SNAKE_HEAD_LEFT, CELL_SNAKE_HEAD_LEFT)
      (CHAR_SNAKE_HEAD_RIGHT, CELL_SNAKE_HEAD_RIGHT)
      (CHAR_SNAKE_HEAD_UP, CELL_SNAKE_HEAD_UP)
      (CHAR_SNAKE_HEAD_DOWN, CELL_SNAKE_HEAD_DOWN)
      (CHAR_FOOD, CELL_FOOD)
      (CHAR_POO, CELL_POO);

    return map.count(ch) == 0 ? CELL_SNAKE_BODY : map.at(ch); // too many different body chars
  }

}

class GameStateLoader::Impl
{
public:
  Board LoadBoard(const std::wstring &message)
  {
    const std::wstring beginning = L"board=";
    if (message.size() < beginning.size() || message.substr(0, beginning.size()) != beginning)
      throw std::runtime_error("can't load board");

    const std::wstring board_str = message.substr(beginning.size());
    size_t size = static_cast<size_t>(sqrt(board_str.size()));
    if (size * size != board_str.size())
      throw std::runtime_error("only square boards are supported");

    Board board(size, size);

    auto it = board_str.cbegin();
    for (size_t y = 0; y < size; ++y)
    {
      for (size_t x = 0; x < size; ++x)
      {
        board.SetCell(x, y, CellFromChar(*it++));
      }
    }

    return board;
  }
};

GameStateLoader::GameStateLoader()
  : p_impl_(new Impl())
{
}

GameStateLoader::~GameStateLoader()
{
  delete p_impl_;
}

Board GameStateLoader::LoadBoard(const std::wstring &message)
{
  return p_impl_->LoadBoard(message);
}
